import ij.ImagePlus;
import ij.plugin.PlugIn;
import ij.process.ColorProcessor;
import ij.process.ImageProcessor;


import java.awt.Color;


import java.awt.Font;

import java.awt.Polygon;
import java.io.PrintStream;

import java.util.ArrayList;
import java.util.Locale;
import java.util.Random; 

import javax.print.attribute.standard.Sides;



import pluginUtils.AGCircle;
import pluginUtils.Circle;
import pluginUtils.Complexe; 
import pluginUtils.MobiusTransform;
import pluginUtils.CircleInversion;
import pluginUtils.SoddyTriplet;


public class Steiner4 extends ImagePlus implements PlugIn{
	{Locale.setDefault(Locale.US); }
	
	private ImageProcessor primeIP; 
	private int X=10000,Y=10000; // image size
	private double a=0.6;
	private double b=0.1;
	
	private ArrayList<Circle> candidats=new ArrayList<Circle>();
	private ArrayList<Circle> auTotal=new ArrayList<Circle>();
	private boolean firstTime=true; 
	private int color=2; 
	private Random gene=new Random();
	private int alternance[]={5,5,3,8,3,4,6,15,5,14}; 
	
	
//	 Inverter
	public Circle invCircle=new Circle(Complexe.createComplexeFromRI(2,4),1); 
	//public CircleInversion cinv=new CircleInversion(invCircle);
	
	//private PrintStream output; 
	private Random generator=new Random(); // One always needs some randomness
	
	// For the scaling of the image
	private double x0=0,x1=1,y0=0,y1=1;
	private double acoef,ccoef,vcoef,wcoef; 
	
	private String[] palette={"Yellow","Red","Blue","Green","White","NavyBlue","Turquoise","Orange"};
	private Color[] palette1={Color.YELLOW,Color.RED,Color.BLUE,Color.GREEN,Color.MAGENTA,Color.ORANGE,Color.BLACK};
	public void setCoefs(){
		acoef=X/(x1-x0); 
		vcoef=Y/(y1-y0); 
		ccoef=-acoef*x0; 
		wcoef=-vcoef*y0; 
	}
	 
	
	
	public void setBounds(double xl,double xu,double yl,double yu){
		x0=xl; 
		x1=xu;
		y0=yl;
		y1=yu; 
	}
	
	private int compte=0; 
	
	
	private void drawColoredCircle(Circle c,int i){
	
		double xCenter=c.getCenter().getReal(); 
		double yCenter=c.getCenter().getIm(); 
		//if(c.getRadius()<0.001) return ; 
		compte++; 
		int intX=(int)((xCenter*acoef)+ccoef);
		int intY=(int)((yCenter*vcoef)+wcoef);
		int intRx=(int)(c.getRadius()*acoef);
		int intRy=(int)(c.getRadius()*vcoef);
		
		primeIP.setColor(palette1[i]); 
		primeIP.drawOval(intX-intRx,intY-intRy,intRx*2,intRy*2);
		
		String s="sphere{\n<"+xCenter+","+yCenter+",0>,";
		s+=c.getRadius(); 
		s+=" texture{Texture"+i+"}finish{Finish"+i+"}}"; 
		
		//output.println(s);
		
	}
	

	private void drawPoint(Complexe c){
		double xCenter=c.getReal(); 
		double yCenter=c.getIm(); 
		int intX=(int)(xCenter*acoef+ccoef);
		int intY=(int)(yCenter*vcoef+wcoef);
		primeIP.fillOval(intX-10,intY-10,20,20);
	}
	
	
	
	
	
	private void drawLine(double xd,double yd,double xf,double yf){
		int XD=(int)(xd*acoef+ccoef); 
		int YD=(int)(yd*vcoef+wcoef); 
		int XF=(int)(xf*acoef+ccoef); 
		int YF=(int)(yf*vcoef+wcoef);
		primeIP.drawLine(XD,YD,XF,YF); 
	}
	
	private void steiner(Circle A, int nCircle, int level){
		if(level<0) return; 
	
		double externalRadius=A.getRadius(); 
		double internalRadius=Math.exp(Math.log(externalRadius)-2*Math.log(Math.tan(Math.PI/4+(Math.PI/(2*nCircle)))));
		Circle internal=new Circle(A.getCenter(),internalRadius); 
		
		
		double distance=(externalRadius+internalRadius)/2; 
		double rayon=(externalRadius-internalRadius)/2;
		double xs=A.getCenter().getReal(); 
		double ys=A.getCenter().getIm(); 
		Circle imageOfA=A;
		double xshift=imageOfA.getXcenter(); 
		double yshift=imageOfA.getYcenter();
		double scale=A.getRadius()/imageOfA.getRadius();
		auTotal.add(A); 
		//drawColoredCircle(A,color); 
		AGCircle newExternal=new AGCircle(A.getCenter(),A.getRadius(),true,0);
		
		internal.setXCenter(scale*(internal.getXcenter()-xshift)+xs);
		internal.setYCenter(scale*(internal.getYcenter()-yshift)+ys); 
		internal.setRadius(scale*internal.getRadius()); 
		AGCircle newInternal=new AGCircle(internal.getCenter(),internal.getRadius(),false,0);
		Complexe calcul=Complexe.sub(internal.getCenter(),A.getCenter()); 
		
	    steiner(internal,alternance[level],level-1); 
		
	    AGCircle round[]=new AGCircle[nCircle]; 
	    
		for(int i=0;i<nCircle;i++){ 
			double x=distance*Math.cos(2*i*Math.PI/nCircle); 
			double y=distance*Math.sin(2*i*Math.PI/nCircle); 
			Circle couronne=new Circle(Complexe.createComplexeFromRI(x+xs,y+ys),rayon);
			
			couronne.setXCenter(scale*(couronne.getXcenter()-xshift)+xs);
			couronne.setYCenter(scale*(couronne.getYcenter()-yshift)+ys); 
			couronne.setRadius(scale*couronne.getRadius()); 
			round[i]=new AGCircle(couronne.getCenter(),couronne.getRadius(),false,0); 
			calcul=Complexe.sub(couronne.getCenter(),A.getCenter());
			
			
			steiner(round[i],alternance[level] ,level-1);
			}
		if(level==0) return; 	
		ArrayList<SoddyTriplet> myLocalSoddyTriplets=new ArrayList<SoddyTriplet>();
		
		for(int i=0;i<nCircle;i++){
			//if(level<6)
			myLocalSoddyTriplets.add(new SoddyTriplet(newExternal, round[i], round[(i+1)%nCircle])); 
			myLocalSoddyTriplets.add(new SoddyTriplet(round[i], round[(i+1)%nCircle], newInternal));	
		}
		double minRad=0.2*1.0/50; 
		while(myLocalSoddyTriplets.size()!=0){
			
			ArrayList<SoddyTriplet> pro=new ArrayList<SoddyTriplet>();
			for(SoddyTriplet toto:myLocalSoddyTriplets){
				AGCircle nouvo=toto.getMini();
				if((nouvo.getRadius()>=0.0005)&&(nouvo.getRadius()<3)){
					auTotal.add(nouvo); 
					//drawColoredCircle(new Circle(nouvo.getCenter(),nouvo.getRadius()),color);
					if(nouvo.getRadius()>minRad){
						Circle golem=new Circle(nouvo.getCenter(),nouvo.getRadius()); 
						candidats.add(golem); 
					//steiner(golem,alternance[level] ,level-1); 
					}
					
					//balance.add(nouvo); 
					AGCircle a=toto.getA(); 
					AGCircle b=toto.getB(); 
					AGCircle c=toto.getC();

				    pro.add(new SoddyTriplet(a,b,nouvo)); 
					pro.add(new SoddyTriplet(a,c,nouvo));
				    pro.add(new SoddyTriplet(c,b,nouvo));		
				} //if
				
			}
			myLocalSoddyTriplets=pro; 
			//System.out.println(pro.size());
			} // while
	
			return; 
			
		}
	
	public void run(String arg) {
		primeIP = new ColorProcessor(X,Y);
		
		
		double limit=6; 
	
		setBounds(-limit,limit,-limit,limit);
		//setBounds(0.97,3.03,-0.03,2.03); 
		setCoefs(); 
		
		
		
		
		
		
		Polygon background=new Polygon(new int[]{0,0,X,Y},new int[]{0,X,Y,0},4);
		primeIP.setColor(Color.WHITE);
		
		primeIP.fillPolygon(background);
		
		
		primeIP.setFont(new Font(null,Font.ITALIC,60));
		primeIP.setColor(Color.BLACK);
		primeIP.setLineWidth(4); 
		
		
		
		
		
		setProcessor("Verifying", primeIP);
		
		drawLine(x0,0,x1,0);
		drawLine(0,y0,0,y1);
		
		try{
			//output=new PrintStream("../pearls/scene/doyle/steiner.txt"); 
			Circle externe=new Circle(Complexe.createComplexeFromRI(4,4),1); 
			
			
			steiner(externe,3,5); 
			
			firstTime=false; 
			
			System.out.println("phase 2");
			for(Circle c:candidats){
				//Complexe calcul=Complexe.sub(c.getCenter(),externe.getCenter()); 
				steiner(c,3,4); 
				
			}
		}
		catch (Exception e) {
			System.out.println("plante "+e); 
		}
		double maxRad=0; 
		Complexe nCenter=Complexe.ZERO; 
		System.out.println("taille "+auTotal.size()); 
		for(Circle c:auTotal){
			if(c.getRadius()>maxRad){
				maxRad=c.getRadius(); 
				nCenter=c.getCenter(); 
			}	
		}
		setBounds(nCenter.getReal()-maxRad-0.01,nCenter.getReal()+maxRad+0.01,nCenter.getIm()-maxRad-0.01,nCenter.getIm()+maxRad+0.01); 
		setCoefs(); 
		for(Circle c:auTotal) drawColoredCircle(c, 6); 
		
		show();
		System.out.println("done "+compte); 
	}	
}
	